#ifndef JAM_RULES_H
#define JAM_RULES_H

#ifdef _MSC_VER
#pragma once
#endif

#include "jam.hpp"

#include "jam_lists.hpp"
#include "detail/stack_array.hpp"
#include <boost/utility/value_init.hpp>
#include <boost/intrusive_ptr.hpp>

class jam_module;
class jam_context;
struct jam_frame;
struct parse_node_base;

///////////////////////////////////////////////////////////////////////////////////////
////// Base class for Jam rules
///////////////////////////////////////////////////////////////////////////////////////
class JAMLIB_EXPORT jam_rule : public ref_count_holder
{
public:
	enum param_type{none=0,plain,one_or_more,vararg,sublist};
	struct param_desc
	{		
		param_type type_;
		jam_string name_;

		jam_list sublist_;
		int num_sub_opt_;
		bool last_subparam_vararg_;

		param_desc() : type_(none), num_sub_opt_(), last_subparam_vararg_() {}
	};

	jam_rule(jam_context *context, jam_module *module, const jam_string &name_=jam_string());
	virtual ~jam_rule(){}

	virtual const jam_string& get_name() const {return name_;}
	virtual jam_string get_file() const {return "(none)";}	
	virtual int get_line() const {return 0;}

	jam_module* get_module() const {return module_;}

	//Invoke rule. Caller must setup the appropriate frame.
	void invoke(jam_frame *frame, jam_list *res);

private:
	virtual void do_invoke(jam_frame *frame,jam_list *res)=0;

	void argument_error(const char* message, jam_frame *frame);
	void print_arguments(std::ostream &str);

protected:
	typedef fixed_size_list<param_desc,JAM_MAX_PARAMS> param_list;
	param_list params_;

	jam_module *module_;
	jam_context *context_;
	jam_string name_;

	void add_param(param_type tp, const jam_string &name);
	void add_optional_param(const jam_string &name);
	param_desc& add_list_param();
	const jam_string& check_name(const jam_string& name);
};
DECLARE_INTRUSIVE_PTR(jam_rule);

#endif //JAM_RULES_H
